require "ruby_parser"

##
# Class that converts ruby into javascript:
# javascript = RubyScript::Convert.new( "def some_method; end" )
#
# puts javascript
# "var some_method = function() {
#   
# }"
##

module RubyScript
  class Convert
    def initialize(ruby)
      @ruby = ruby
      @indent_level = 0
    end
    
    def convert
      convert_unknown(parse_ruby);
    end
    
    private
    def parse_ruby
      RubyParser.new.parse("#{@ruby.gsub(/\r/, "\n")}")
    end
    
    def indent(offset = nil, num = nil)
      if offset.nil?
        "  " * @indent_level
      else
        eval("\"  \" * (@indent_level #{offset.to_s} #{num})")
      end
    end
    
    def convert_unknown(parsed)
      send("convert_#{parsed[0]}", parsed)
    end
    
    def convert_scope(scope)
      convert_unknown(scope[1])
    end
    
    def convert_block(block)
      body = convert_unknown(block[1])
      "#{body}\n"
    end
    
    def convert_defn(defn)
      @indent_level += 1
      func_name = defn[1]
      func_args_return = convert_args(defn[2])
      func_args = func_args_return[0]
      func_args_assign = ""
      func_args_return[1].each {|arg| func_args_assign << convert_arg_assign(arg) }
      func_body = convert_unknown(defn[3])
      rtn = "#{indent(:-, 1)}var #{func_name} = function(#{func_args}) {\n" +
      "#{func_args_assign}" +
      "#{func_body}" +
      "#{indent(:-, 1)}};\n"
      @indent_level -= 1
      rtn
    end
    
    def convert_args(args)
      # TODO: add support for default values
      args.delete_at(0)
      arguments = ""
      assign = []
      args.each_index do |i|
        if args[i].instance_of? Symbol
          arguments << ", " unless i == 0
          arguments << args[i].to_s
        else
          assign << [args[i][1][1], args[i][1][2]] # name, default_value
        end
      end
      [arguments, assign]
    end
    
    def convert_arg_assign(arg_arr) # arg_arr = [name, default_value]
      "#{indent}if (#{arg_arr[0]} == undefined) {\n" +
      "#{indent}  #{arg_arr[0]} = #{convert_unknown(arg_arr[1])};\n" +
      "#{indent}}\n"
    end
    
    def convert_call(call)
      call_name = call[2]
      call_args = convert_arglist(call[3])
      "#{call_name}(#{call_args})"
    end
    
    def convert_arglist(arglist)
      arglist.delete_at(0)
      args = ""
      arglist.each_index do |i|
        args << ", " unless i == 0
        args << convert_unknown(arglist[i])
      end
      args
    end
    
    def convert_nil(nil_arg)
      "null"
    end
    
    def convert_str(str)
      %Q{"#{str[1]}"}
    end
    
    def convert_dstr(dstr)
      dstr.delete_at(0)
      string = ""
      unless dstr[0].empty?
        string << %Q{"#{dstr[0]}"}
        string << " + " unless i == (dstr.count - 1)
      end
      dstr.delete_at(0)
      dstr.each_index do |i|
        string << convert_unknown(dstr[i])
        string << " + " unless i == (dstr.count - 1)
      end
      string
    end
    
    def convert_evstr(evstr)
      "(#{convert_unknown(evstr[1])})"
    end
    
    def convert_lvar(lvar)
      lvar[1].to_s
    end
  end
end